﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using TcpServer.Dao;
using TcpServer.ThreadManage;
using System.Reflection;
using System.Net;
using System.Security;

namespace TcpServer.User
{
    class UserClass
    {
        public TcpClient client { get; set; }
        //public BinaryReader br { get; set; }
        //public BinaryWriter bw { get; set; }
        public string hotelID { get; set; }
        public DateTime LastConnTime { get; set; }
        public string GPSIMEI { get; set; }
        public string MeterIP { get; set; }
        public Thread UserThread { get; set; }
        public NetworkStream NStream { get; set; }
        public byte[] buffer { get; set; }
        public int ListIndexID { get; set; }
        private int NewPort { get;set; }    //1就是新的12345端口，0就是老端口
        public int PortNum { get; set; }
        public int tag { get; set; }    //判断是否为新版数据    0为老板80秒 1为新版80秒，

        private static readonly object obj = new object();
        public UserClass(TcpClient client, int PortNum,int NewPort=0)
        {
            this.client = client;
            NStream = client.GetStream();

            //构造函数需要初始化默认值
            MeterIP = "";
            hotelID = "";
            LastConnTime = DateTime.Now;
            tag = 0;
            //br = new BinaryReader(NStream);
            //bw = new BinaryWriter(NStream);


            //这里 NewPort=1为对应12345 下面需要进入BaseDao2
            //这里 NewPort=2为对应12346 下面需要进入BaseDao_N37 
            //这里 NewPort=3为对应12347 下面需要进入BaseDao_N38 
            this.NewPort = NewPort;
            this.PortNum = PortNum;

        }

        public void Close()
        {
            //br.Close();
            //br.Dispose();
            //bw.Close();
            //bw.Dispose();
            client.Close();
            
            NStream.Close();
            NStream.Dispose();

            Log.Info("User", "在主函数里面Remove相关UserList，脱离的线程ID=" + UserThread.ManagedThreadId.ToString() + ";脱离的MeterIP=" + MeterIP);
            Program.UserList.Remove(this);
            //在此需要用倒叙方式删除相对应的引用
            //for (int i = Program.UserList.Count - 1; i >= 0; i--)
            //{
            //    if (Program.UserList[i].MeterIP == MeterIP)
            //    {
                    
            //        Program.UserList.Remove(Program.UserList[i]);
                    
            //    }
            //}

            try
            {
                UserThread.Abort(); //终止线程
                UserThread.Join();
            }
            catch(ThreadAbortException ex)
            {
                Console.WriteLine("正常捕捉到线程终止异常,线程退出完成,ThreadAbortException:" + ex.Message.ToString());
                Log.Info("User","线程ID="+UserThread.ManagedThreadId.ToString()+ ";正常捕捉到线程终止异常ThreadAbortException,线程退出,");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString()); 
                Console.WriteLine("线程停止失败");
            }
            

            //Program.UserList.RemoveAt(ListIndexID);
        }

        public void ReciveData()
        {
            string ReceiveData = "";
            bool IsInsertSQL = true;  //表明要入库
            if (NStream.CanRead)
            {
                buffer = new byte[2048];
                int count = 0;
                int SentTimes = 0;
                double lat=0, lon=0;
                string can="";
                tag = 0;

                string timestamp1 = "";
                string timestamp2 = "";
                string timestamp3 = "";
                string timestamp4 = "";
                do
                {
                    //string ReceiveString = null;
                    //#MeterIP:00,IMEI:864811034596481,EnergyP_A:244.000000,KeyStatus:000101,SQ:13,Temperature:*
                    //#hotelID:210883,MeterIP:00,IMEI:867793034716649,EnergyP_A:0.000000,KeyStatus:0000000,SQ:22,Temperature:,click:272003,md5:A2CA076DC2CA0A0C0BC7DF09DC901FAC*
                    try
                    {
                        //timestamp1 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms");
                        count = NStream.Read(buffer, 0, 2048);
                        //NStream.Flush();
                        ReceiveData = Encoding.Default.GetString(buffer, 0, count).Trim();
                        
                        Console.WriteLine(ReceiveData+",ip="+GetRemoteIPAndPort(client));
                        Log.Info("User", ReceiveData + ",ip=" + GetRemoteIPAndPort(client)+ ",ReceivePortNum=" + PortNum.ToString() + ",线程ID=" + UserThread.ManagedThreadId.ToString());

                        //timestamp2 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms");

                        if (ReceiveData.Contains("#")&&ReceiveData.Contains("*"))
                        {
                            int index_1 = ReceiveData.IndexOf("#");
                            //下面要计算第一个#号过后的*号位置
                            int index_2 = ReceiveData.Substring(index_1 + 1).IndexOf("*") + 1 + index_1;

                            IsInsertSQL = true;
                            ReceiveData = ReceiveData.Substring(index_1, index_2 - index_1 + 1);
                            //传输的数据有在#*之外还有数据，所以这一帧数据有问题
                            if (ReceiveData.Length != (index_2 - index_1 + 1))
                            {
                                Console.WriteLine("该次收到的数据不准确,已裁剪至第一组#与*之间的数据,请看上面的接收日志");                           
                                Log.Info("User", "该次收到的数据不准确,已裁剪至第一组#与*之间的数据,请看上面的接收日志,线程ID = " + UserThread.ManagedThreadId.ToString());
                                Log.Info("User","截取后的数据：" + ReceiveData + "。线程ID=" + UserThread.ManagedThreadId.ToString());
                            }
                            
                            //LoRaID = "LoRa01";
                            ReceiveData = ReceiveData.Replace("#","");
                            ReceiveData = ReceiveData.Replace("*", "");
                            string[] sArray = ReceiveData.Split(',');


                            //判断是否为2018-4月，叶军新版发射数据
                            //这里判断下数据的正确性
                            string GetStringValues = "";
                            string[] GetStringArray = sArray;
                            string MCUmd5 = "";//单片机发上来的MD5
                            string state = "";  //服务器向单片机发指令，然后单片机上传的返回值
                            
                            int answer = 0;     //下位机的返回应答。#hotelID:210883,MeterIP:03,SQ:16,click:65536,state:OK*
                            string SqlValue = "";

                            for (int i = 0; i < GetStringArray.Length; i++)
                            {
                                
                                if (GetStringArray[i].Contains("hotelID:"))
                                {
                                    tag = 1;
                                    hotelID = GetStringArray[i].Replace("hotelID:", "");
                                    GetStringValues += GetStringArray[i].Replace("hotelID:", "");
                                    SqlValue += "hotelID:" + GetStringArray[i].Replace("hotelID:", "") + ",";

                                }
                                if (GetStringArray[i].Contains("MeterIP:"))
                                {
                                    GetStringValues += GetStringArray[i].Replace("MeterIP:", "");
                                    SqlValue += "MeterIP:" + GetStringArray[i].Replace("MeterIP:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("IMEI:"))
                                {
                                    GetStringValues += GetStringArray[i].Replace("IMEI:", "");
                                    SqlValue += "IMEI:" + GetStringArray[i].Replace("IMEI:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("EnergyP_A:"))
                                {
                                    GetStringValues += GetStringArray[i].Replace("EnergyP_A:", "");
                                    SqlValue += "EnergyP_A:" + GetStringArray[i].Replace("EnergyP_A:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("KeyStatus"))
                                {
                                    GetStringValues += GetStringArray[i].Replace("KeyStatus:", "");
                                    SqlValue += "KeyStatus:" + GetStringArray[i].Replace("KeyStatus:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("SQ:"))
                                {
                                    GetStringValues += GetStringArray[i].Replace("SQ:", "");
                                    SqlValue += "SQ:" + GetStringArray[i].Replace("SQ:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("Temperature:"))
                                {
                                    //这里不用加密，只是为了兼容老数据
                                    SqlValue += "Temperature:" + GetStringArray[i].Replace("Temperature:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("OpenDoorFlag:"))
                                {
                                    //这里不参与编程
                                    //GetStringValues += GetStringArray[i].Replace("OpenDoorFlag:", "");
                                    SqlValue += "OpenDoorFlag:" + GetStringArray[i].Replace("OpenDoorFlag:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("PW:"))
                                {
                                    GetStringValues += GetStringArray[i].Replace("PW:", "");
                                    SqlValue += GetStringArray[i].Replace("PW:", "") + ",";
                                }
                                if (GetStringArray[i].Contains("click:"))
                                {
                                    GetStringValues += GetStringArray[i].Replace("click:", "");
                                }
                                if (GetStringArray[i].Contains("md5:"))
                                {
                                    MCUmd5 = GetStringArray[i].Replace("md5:", "");

                                }
                                if (GetStringArray[i].Contains("state:"))
                                {
                                    state = GetStringArray[i].Replace("state:", "");
                                    answer = 1;

                                }

                            }

                            //MD5加密验证  以下判断为新版的数据格式
                            if (hotelID != "" && state == "") 
                            {
                                System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider();
                                byte[] bytResult = md5.ComputeHash(Encoding.Default.GetBytes(GetStringValues));
                                string strResult = BitConverter.ToString(bytResult);
                                strResult = strResult.Replace("-", "");

                                if (strResult != MCUmd5)
                                {
                                    //这里说明加密认证失败，记录下，然后舍去以后的操作
                                    Log.Info("User", "MD5验证数据出错,MeterIP=" + MeterIP + ",线程ID = " + UserThread.ManagedThreadId.ToString());
                                    string sqltemp1 = "insert into yejunTcpLog (log,logtime) values('MD5验证数据出错,可以对应LOG文件日志去查原数据,线程ID=" + UserThread.ManagedThreadId.ToString() + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "')";
                                    BaseDao.execute(sqltemp1);
                                    LastConnTime = DateTime.Now;
                                    IsInsertSQL = false;
                                    NStream.Flush();
                                    continue;
                                     
                                }
                            }
                            



                            //int i = 1;
                            for (int i = 0; i < sArray.Length; i++)
                            {
                                if (sArray[i].Contains("EnergyP_A:"))
                                {
                                    string tempEnergy = sArray[i].Replace("EnergyP_A:", "");
                                    if (!tempEnergy.Contains("000000"))
                                    {
                                        Log.Info("User", "判断到获取的EnergyP_A数据格式有问题，关闭长连接,MeterIP="+ MeterIP + ",线程ID = " + UserThread.ManagedThreadId.ToString());
                                        string sqltemp1 = "insert into yejunTcpLog (log,logtime) values('判断到获取的EnergyP_A数据格式有问题,不包含六个0;可以对应LOG文件日志去查原数据,线程ID=" + UserThread.ManagedThreadId.ToString() + "','"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")+"')";
                                        BaseDao.execute(sqltemp1);
                                        IsInsertSQL = false;
                                        //Close();

                                    }
                                }
                            }


                           
                            if (SentTimes == 0)     //说明是第一次接受数据
                            {
                                //需要做判断先取出MeterIP，查看其它UserClient里面有不有当前的设备IP地址
                                for (int i2 = 0; i2 < sArray.Length; i2++)
                                {
                                    if (sArray[i2].Contains("MeterIP:"))
                                    {
                                        string tempMeterIP = sArray[i2].Replace("MeterIP:", "");
                                        //找到对应的IP信息其它IP信息
                                        try
                                        {
                                            //Program.UserList.Find(s => s.MeterIP.Equals(tempMeterIP)).Close();
                                            //这里访问的MeterIP不能为Null
                                            List < UserClass > PreDelList = Program.UserList.FindAll(delegate (UserClass s) {
                                                return s.MeterIP.Contains(tempMeterIP);
                                            });
                                            if (PreDelList.Count > 0)
                                            {
                                                Console.WriteLine("第一次连接上来，但是找到与之前的MeterIP相同，具体查LOG文件日志"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                                                Log.Error("User", "线程ID=" + UserThread.ManagedThreadId.ToString() + ";第一次连接上来，但是找到之前的MeterIP相同,一共找到"+PreDelList.Count.ToString()+"个连接相同");
                                                for (int j1 = 0; j1 < PreDelList.Count; j1++)
                                                {
                                                    //PreDelList[j1].Close();
                                                    //string sqltemp1 = "insert into yejunTcpLog (log,logtime) values('del TCP MeterIP=" + tempMeterIP + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "')";
                                                    //BaseDao.execute(sqltemp1);
                                                    //Console.WriteLine("已记录踢掉MeterIP="+tempMeterIP+"的TCP连接在YejunTcpLog");
                                                }
                                            }
                                            else
                                            {
                                                Console.WriteLine("第一次连接上来，没有对应的MeterIP");
                                            }

                                        }
                                        catch (NullReferenceException ex)
                                        {
                                            Console.WriteLine("NullReferenceException捕捉到了，记录在YejunTcpLog");
                                            string sqltemp1 = "insert into yejunTcpLog (log,logtime) values('"+ex.ToString().Replace("'","")+"','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "')";
                                            BaseDao.execute(sqltemp1);

                                        }
                                        catch (Exception ex)
                                        {
                                            Console.WriteLine(ex.ToString());
                                            Console.WriteLine("Exception捕捉到了，记录在YejunTcpLog");
                                            string sqltemp1 = "insert into yejunTcpLog (log,logtime) values('" + ex.ToString().Replace("'", "") + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "')";
                                            BaseDao.execute(sqltemp1);
                                        }
                                        MeterIP = tempMeterIP;
                                    }
                                }
                            }
                            else
                            {

                                //需要做判断先取出MeterIP，查看该连接发上来的MeterIP是不是和上一次的一样
                                for (int i2 = 0; i2 < sArray.Length; i2++)
                                {
                                    if (sArray[i2].Contains("MeterIP:"))
                                    {
                                        string tempMeterIP = sArray[i2].Replace("MeterIP:", "");
                                        //找到对应的IP信息其它IP信息

                                        //如果和原来的不相等，则需要做删除该长连接的操作，并做记录
                                        if(MeterIP != tempMeterIP)
                                        {
                                            string sqltemp1 = "insert into yejunTcpLog (log,logtime) values('该条信息判断到数据异常;MeterIP不一样;可以对应LOG文件日志去查原数据,线程ID=" + UserThread.ManagedThreadId.ToString() + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "')";
                                            BaseDao.execute(sqltemp1);
                                            Console.WriteLine("连接发上来的数据异常，可查LOG文件日志"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")+"，该条数据不进数据库");
                                            LastConnTime = DateTime.Now;
                                            continue;
                                        }   
                                    
                                    }
                                }
                            }
                                
                            
                            SentTimes++;
                            string sql = "";
                            if (tag == 0)   //说明是老板本数据
                            {
                                sql = "insert into yejun1 (log,logtime,ipaddress) values('" + ReceiveData + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + GetRemoteIPAndPort(client) + ",PortNum=" + PortNum.ToString() + "')";
                            }
                            else
                            {
                                sql = "insert into yejun1 (log,logtime,ipaddress) values('" + SqlValue.Substring(0, SqlValue.Length - 1) + "','" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "','" + GetRemoteIPAndPort(client) + ",PortNum=" + PortNum.ToString() + "')";
                            }

                            timestamp3 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms");

                            lock (obj)
                            {
                                if (IsInsertSQL)
                                {
                                    if (answer == 0)    //表明不是应答 需要进库
                                    {
                                        if (NewPort == 0)
                                        {
                                            BaseDao.execute(sql);
                                        }
                                        else if (NewPort == 1)  //这里表明该连接是从12345端口引入，所以需要存入新数据库
                                        {
                                            //#hotelID:210884,MeterIP:000,IMEI:869270042363530,EnergyP_A:0.000000,KeyStatus:000101,SQ:13,Temperature:,click:1794238,md5:6DEF2B9BF2E7A2FE38C8BE2BE1BF2A7D*
                                            BaseDao2.execute(sql);
                                        }
                                        else if (NewPort == 2)  //这里 NewPort=2为对应12346 下面需要进入BaseDao_N37 
                                        {
                                            BaseDao_N37.execute(sql);
                                        }
                                        else if (NewPort == 3)  //这里 NewPort=3为对应12347 下面需要进入BaseDao_N38 
                                        {
                                            BaseDao_N38.execute(sql);
                                        }
                                        else if(NewPort == 4)   //这里 NewPort=3为对应12348 下面需要进入BaseDao_N39
                                        {
                                            BaseDao_N39.execute(sql);
                                        }
                                        else if(NewPort == 5)   //这里 NewPort=3为对应12349 下面需要进入BaseDao_N40
                                        {
                                            BaseDao_N40.execute(sql);
                                        }

                                        //BaseDao.execute(sql);
                                    }
                                    else
                                    {
                                        sql = "insert into t_o_ReturnStr (ReturnStr,GetTime) values ('" +
                                            ReceiveData + ",ip=" + GetRemoteIPAndPort(client) + ",ReceivePortNum=" + PortNum.ToString() + ",线程ID=" + UserThread.ManagedThreadId.ToString() + "','" + DateTime.Now.ToString() + "')";
                                        BaseDao.execute(sql);
                                    }
                                    
                                    //Thread.Sleep(50);
                                }

                            }

                            LastConnTime = DateTime.Now;
                            IsInsertSQL = true;

                            timestamp4 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ms");
                        }
                        //else
                        //{
                        //    Log.Error("User", "发送的数据不包含*#,调用Close方法移除该线程");
                        //    Close();

                        //}

                        Log.Info("TimeStamp", "hotelID=" + hotelID + ",MeterIP=" + MeterIP + ",time3=" + timestamp3 + ",time4=" + timestamp4);


                    }
                    catch (ThreadAbortException ex)
                    {
                        Console.WriteLine("捕捉线程终止异常,ThreadAbortException:" + ex.Message.ToString());
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message.ToString());
                        Log.Info("User", "处理ReceiveData的时候抛出catch，ex=" + ex.Message.ToString() + ",处理后的数据为:" + ReceiveData);
                        Close();
                        break;
                    }
                    //下面的判断为，没有可读数据，并且计数返回的是0 表示客户端有意断开了。NStream.DataAvailable || count!=0
                    NStream.Flush();
                    
                } while (client.IsOnline());
            }
            client.Close();
            NStream.Close();
            NStream.Dispose();
            Program.UserList.Remove(this);
            Console.WriteLine("线程退出");
            Log.Info("User", "线程ID=" + UserThread.ManagedThreadId.ToString() + ";线程最后退出");

        }

        public void SendData(String data)
        {
            try
            {
                if (NStream.CanWrite)
                {
                    byte[] WriteBuffer = Encoding.Default.GetBytes(data);
                    NStream.Write(WriteBuffer, 0, WriteBuffer.Length);
                    Console.WriteLine("向"+MeterIP+"发送数据完成");
                    Console.WriteLine("数据为:" + data);
                }
                else
                {
                    Console.WriteLine("Sorry.  You cannot write to this NetworkStream.");
                    Log.Error("SendDataError", "MeterIP="+MeterIP+"，向其发送数据失败失败");
                }   
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message.ToString());
                Log.Info("User", "处理SendData的时候抛出catch，ex=" + ex.Message.ToString());
                Close();

            }
        
        }


        public string GetRemoteIPAndPort(TcpClient cln)
        {

            IPEndPoint ip=(IPEndPoint)cln.Client.RemoteEndPoint;
            string ipaddress = ip.Address.ToString();
            string port = ip.Port.ToString();
            return ipaddress + ":" + port;
        }
       
    }
    public static class TcpClientEx
    {
        public static bool IsOnline(this TcpClient c)
        {
            return !((c.Client.Poll(1000, SelectMode.SelectRead) && (c.Client.Available == 0)) || !c.Client.Connected);
        }
    }
}
